#include <iostream>

#include "atom.h"
#include "node.h"
#include "reference.h"
#include "tree.h"
#include "tuple.h"

Node *Node::final () {
  Node *node = this, *next;
  while(typeid(*node) == typeid(Reference)) {
    next = static_cast<Reference*> (node)->target;
    if(next)
      node = next;
    else
      break;
  }
  return node;
}

pNode Node::replicate () {
  return this;
}

pNode::pNode (const std::string &s) {
  node = Node::parse (s);
  node->referenceCount++;
}

// Finds ordinality. Returns zero if equal; negative or positive when first
//  node is smaller or larger than the second respectively
int compareNodes (Node &node1, Node &node2) {
  Node::NodeType nt1 = node1.type (), nt2 = node2.type ();

  if(nt1 != nt2) {
    std::cerr << "[ERROR] Comparing different data types" << std::endl;
    return 0;
  }

  switch(nt1) {
  case Number::ID:
    return static_cast<Number&> (node1).value
        - static_cast<Number&> (node2).value;

  case Atom::ID:
  case String::ID:
    return strcmp (static_cast<Atom&> (node1).name.c_str (),
        static_cast<Atom&> (node2).name.c_str ());

  case Tree::ID: {
    Tree &t1 = static_cast<Tree&> (node1), &t2 = static_cast<Tree&> (node2);
    int diff = strncmp (t1.ops, t2.ops, 2);
    if(!diff)
      diff = compareNodes (*t1.left, *t2.left);
    if(!diff)
      diff = compareNodes (*t1.right, *t2.right);
    return diff;
  }

  case Tuple::ID: {
    Tuple &t1 = static_cast<Tuple&> (node1), &t2 = static_cast<Tuple&> (node2);
    int size = t1.nodes.size (), i = 0;
    int diff = size - t2.nodes.size ();
    while(!diff && i < size) {
      diff = compareNodes (*t1.nodes[i], *t2.nodes[i]);
      i++;
    }
    return diff;
  }
  default:
    std::cerr << "[ERROR] Comparing invalid data type" << std::endl;
    return 0;
  }
}

bool isTrue (Node &node) {
  if(typeid(node) == typeid(Atom)) {
    std::string &name = static_cast<Atom&> (node).name;
    char c = name.empty () ? 0 : toupper(name[0]);
    return c == 'T' || c == 'Y';
  } else if(typeid(node) == typeid(Number))
    return static_cast<Number&> (node).value != 0;
  else
    return false;
}
